/**
 * wifi热点配网模块
 * - 初始化
 * - 配网结束,自动关闭该模块
 */
#include <string.h>
#include <stdio.h>
#include "app_config.h"
#include "system/includes.h"
#include "wifi/wifi_connect.h"
#include "lwip.h"
#include "dhcp_srv/dhcp_srv.h"

#include "wifi_prov_mgr_v1.h"
#include "wifi_prov_server_v1.h"

// ---------- 日志 ----------
#define PLOG(fmt, ...)                        \
    do                                        \
    {                                         \
        printf("[prov] " fmt, ##__VA_ARGS__); \
    } while (0)

typedef struct
{
    OS_MUTEX mtx;
    uint8_t inited;
    wifi_prov_nv_t nv;
    wifi_prov_desired_mode_t desired;
    wifi_prov_cfg_t config;

    int save_ssid_flag;
} prov_ctx_t;

static prov_ctx_t g;

static void server_on_credentials(const char *ssid, const char *pwd, int save);

static wifi_prov_server_cfg_t prov_server_cfg = {
    .port = 80,
    .captive_portal_ip = 0xC0A80801,
    .cb = server_on_credentials,
};

// ---------- 存储模块 start ----------
static u32 prov_crc32(const void *data, u32 len)
{
    const uint8_t *p = (const uint8_t *)data;
    u32 c = 0xFFFFFFFF;
    for (u32 i = 0; i < len; i++)
    {
        c ^= p[i];
        for (int j = 0; j < 8; j++)
        {
            u32 m = -(c & 1);
            c = (c >> 1) ^ (0xEDB88320 & m);
        }
    }
    return ~c;
}

// ---------- syscfg NV ----------
static int nv_default(wifi_prov_nv_t *nv)
{
    memset(nv, 0, sizeof(*nv));
    nv->magic = WIFI_PROV_MAGIC_NEED_PROV;
    nv->crc = prov_crc32(nv, sizeof(*nv) - 4);
    return 0;
}

static int wifi_prov_nv_load(wifi_prov_nv_t *out)
{
    if (!out)
        return -1;

    wifi_prov_nv_t tmp;
    memset(&tmp, 0, sizeof(tmp));
    int r = syscfg_read(WIFI_PROV_CFG_ID, &tmp, sizeof(tmp));
    if (r != sizeof(tmp))
    {
        nv_default(&tmp);
        *out = tmp;
        return 1;
    }

    u32 crc = prov_crc32(&tmp, sizeof(tmp) - 4);
    if (crc != tmp.crc)
    {
        nv_default(&tmp);
        *out = tmp;
        return 2;
    }

    *out = tmp;
    return 0;
}

static int nv_save(const wifi_prov_nv_t *in)
{
    wifi_prov_nv_t tmp = *in;
    tmp.crc = prov_crc32(&tmp, sizeof(tmp) - 4);
    return (syscfg_write(WIFI_PROV_CFG_ID, &tmp, sizeof(tmp)) == sizeof(tmp)) ? 0 : -2;
}

static int nv_set_magic(u32 magic)
{
    wifi_prov_nv_t nv;
    wifi_prov_nv_load(&nv);
    nv.magic = magic;
    return nv_save(&nv);
}

static int nv_set_sta_info(const char *ssid, const char *pwd)
{
    os_mutex_pend(&g.mtx, 0);
    wifi_prov_nv_t nv = g.nv;
    if (ssid)
        strncpy(nv.ssid, ssid, sizeof(nv.ssid) - 1);
    if (pwd)
        strncpy(nv.pwd, pwd, sizeof(nv.pwd) - 1);
    nv.magic = WIFI_PROV_MAGIC_NORMAL;
    int ret = nv_save(&nv);
    if (ret == 0)
        g.nv = nv;
    os_mutex_post(&g.mtx);

    PLOG("nv_set_sta_info: %d\r\n", ret);
    return ret;
}
// ---------- 存储模块 end ----------

// ---------- SDK STA存储清理 ----------
static void sdk_clear_sta_info(void)
{
    // 清理SDK wifi信息

    uint8_t ssid_stored_cnt;
    struct wifi_stored_sta_info sta_info[32];

    ssid_stored_cnt = wifi_get_stored_sta_info(sta_info);
    for (int i = 0; i < ssid_stored_cnt; i++)
    {
        PLOG("wifi_get_stored_sta_info[%d]= %s \r\n", i, sta_info[i].ssid);
        wifi_del_stored_sta_info((char *)sta_info[i].ssid);
    }
}

static void server_on_credentials(const char *ssid, const char *pwd, int save)
{
    // 切换到STA模式
    g.save_ssid_flag = save;
    wifi_set_sta_connect_best_ssid(0);
    wifi_enter_sta_mode(ssid, pwd);
}

// ---------- 决策 desired 模式 ----------
static void decide_desired_locked(void)
{
    if (g.nv.magic == WIFI_PROV_MAGIC_FACTORY)
    {
        PLOG("factory\n");
        if (g.config.ops.on_clear_sta_info)
            g.config.ops.on_clear_sta_info();

        nv_default(&g.nv);
        nv_save(&g.nv); // 置 NEED_PROV
    }
    g.desired = (g.nv.magic == WIFI_PROV_MAGIC_NEED_PROV) ? WIFI_PROV_DESIRED_AP : WIFI_PROV_DESIRED_STA;
}

int wifi_prov_mgr_init(wifi_prov_cfg_t *cfg)
{
    if (!g.inited)
    {
        memset(&g, 0, sizeof(g));
        os_mutex_create(&g.mtx);
        g.inited = 1;
        g.save_ssid_flag = 0;
    }

    os_mutex_pend(&g.mtx, 0);
    if (cfg)
        g.config = *cfg;
    else
        g.config = (wifi_prov_cfg_t)WIFI_PROV_DEFAULT_CONFIG();

    wifi_prov_nv_load(&g.nv);
    decide_desired_locked();
    os_mutex_post(&g.mtx);

    return 0;
}

int wifi_prov_mgr_trigger_ap(void)
{
    if (nv_set_magic(WIFI_PROV_MAGIC_NEED_PROV) != 0)
        return -1;
    os_time_dly(10);
    system_reset();
    return 0;
}

int wifi_prov_mgr_factory_reset(void)
{
    sdk_clear_sta_info();
    wifi_prov_nv_t nv;
    nv_default(&nv); // NEED_PROV
    nv_save(&nv);
    os_time_dly(10);
    system_reset();
    return 0;
}

wifi_prov_desired_mode_t wifi_prov_mgr_get_desired_mode(void)
{
    os_mutex_pend(&g.mtx, 0);
    wifi_prov_desired_mode_t d = g.desired;
    os_mutex_post(&g.mtx);
    return d;
}

int wifi_prov_mgr_on_wifi_evt(void *network_ctx, enum WIFI_EVENT event)
{
    switch (event)
    {
        case WIFI_EVENT_MODULE_INIT:
            {
                os_mutex_pend(&g.mtx, 0);
                // wifi模式决策
                if (g.desired == WIFI_PROV_DESIRED_STA)
                {
                    // sta模式,由用户配置
                    if (g.nv.ssid[0] && g.config.ops.on_wifi_sta_mode)
                    {
                        g.config.ops.on_wifi_sta_mode(g.nv.ssid, g.nv.pwd);
                    }
                }
                else
                {
                    // 其他模式都是进入配网模式
                    // ap模式
                    g.config.ops.on_wifi_ap_mode();
                }
                os_mutex_post(&g.mtx);
            }
            break;

        case WIFI_EVENT_AP_START:
            // 启动配网服务器
            PLOG("Start http server.\n");
            wifi_prov_server_start(&prov_server_cfg);
            if (g.config.ops.on_start)
                g.config.ops.on_start();
            break;

        case WIFI_EVENT_AP_STOP:
            if (g.config.ops.on_stop)
                g.config.ops.on_stop();
            break;

        case WIFI_EVENT_STA_NETWORK_STACK_DHCP_SUCC:
            {

                if (g.save_ssid_flag)
                {
                    g.save_ssid_flag = 0;
                    PLOG("wifi_store_mode_info save\n");

                    // 覆盖默认SSID
                    struct wifi_mode_info info = {0};
                    info.mode = STA_MODE;
                    wifi_get_mode_cur_info(&info);
                    printf("+++++++ ssid: %s, pwd: %s\n", info.ssid, info.pwd);
                    wifi_store_mode_info(STA_MODE, info.ssid, info.pwd);

                    // 存储自定义SSID
                    nv_set_sta_info(info.ssid, info.pwd);
                }

                if (g.config.ops.on_finished && (g.desired == WIFI_PROV_DESIRED_AP))
                {
                    g.config.ops.on_finished();
                    // AP模式,直接结束
                    wifi_prov_server_stop();
                }
            }
            break;

        case WIFI_EVENT_STA_CONNECT_ASSOCIAT_TIMEOUT:
        case WIFI_EVENT_STA_CONNECT_TIMEOUT_NOT_FOUND_SSID:
        case WIFI_EVENT_STA_CONNECT_ASSOCIAT_FAIL:
        case WIFI_EVENT_STA_NETWORK_STACK_DHCP_TIMEOUT:
            {
                sdk_clear_sta_info();
                wifi_prov_nv_t nv;
                nv_default(&nv); // NEED_PROV
                nv_save(&nv);

                if (g.config.ops.on_timeout)
                    g.config.ops.on_timeout();
            }
            break;
    }

    return 0;
}
